home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / non-ANSI / c-client / tcp_unix.c < prev    next >
C/C++ Source or Header  |  1997-02-21  |  17KB  |  575 lines

  1. /*
  2.  * Program:    UNIX TCP/IP routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    1 August 1988
  13.  * Last Edited:    21 February 1997
  14.  *
  15.  * Copyright 1997 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made available
  24.  * "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36. #undef write            /* don't use redefined write() */
  37.  
  38.                 /* TCP timeout handler routine */
  39. static tcptimeout_t tcptimeout = NIL;
  40.                 /* TCP timeouts, in seconds */
  41. static long tcptimeout_open = 0;
  42. static long tcptimeout_read = 0;
  43. static long tcptimeout_write = 0;
  44. static long rshtimeout = 15;    /* rsh timeout */
  45.  
  46. /* TCP/IP manipulate parameters
  47.  * Accepts: function code
  48.  *        function-dependent value
  49.  * Returns: function-dependent return value
  50.  */
  51.  
  52. void *tcp_parameters (function,value)
  53.     long function;
  54.     void *value;
  55. {
  56.   switch ((int) function) {
  57.   case SET_TIMEOUT:
  58.     tcptimeout = (tcptimeout_t) value;
  59.     break;
  60.   case GET_TIMEOUT:
  61.     value = (void *) tcptimeout;
  62.     break;
  63.   case SET_OPENTIMEOUT:
  64.     tcptimeout_open = (long) value;
  65.     break;
  66.   case GET_OPENTIMEOUT:
  67.     value = (void *) tcptimeout_open;
  68.     break;
  69.   case SET_READTIMEOUT:
  70.     tcptimeout_read = (long) value;
  71.     break;
  72.   case GET_READTIMEOUT:
  73.     value = (void *) tcptimeout_read;
  74.     break;
  75.   case SET_WRITETIMEOUT:
  76.     tcptimeout_write = (long) value;
  77.     break;
  78.   case GET_WRITETIMEOUT:
  79.     value = (void *) tcptimeout_write;
  80.     break;
  81.   case SET_RSHTIMEOUT:
  82.     rshtimeout = (long) value;
  83.     break;
  84.   case GET_RSHTIMEOUT:
  85.     value = (void *) rshtimeout;
  86.     break;
  87.   default:
  88.     value = NIL;        /* error case */
  89.     break;
  90.   }
  91.   return value;
  92. }
  93.  
  94. /* TCP/IP open
  95.  * Accepts: host name
  96.  *        contact service name
  97.  *        contact port number
  98.  * Returns: TCP/IP stream if success else NIL
  99.  */
  100.  
  101. TCPSTREAM *tcp_open (host,service,port)
  102.     char *host;
  103.     char *service;
  104.     long port;
  105. {
  106.   TCPSTREAM *stream = NIL;
  107.   int i,sock,flgs;
  108.   char *s;
  109.   struct sockaddr_in sin;
  110.   struct hostent *host_name;
  111.   char hostname[MAILTMPLEN];
  112.   char tmp[MAILTMPLEN];
  113.   fd_set fds,efds;
  114.   struct protoent *pt = getprotobyname ("ip");
  115.   struct servent *sv = service ? getservbyname (service,"tcp") : NIL;
  116.   struct timeval tmo;
  117.   tmo.tv_sec = tcptimeout_open;
  118.   tmo.tv_usec = 0;
  119.   FD_ZERO (&fds);        /* initialize selection vector */
  120.   FD_ZERO (&efds);        /* handle errors too */
  121.   if (s = strchr (host,':')) {    /* port number specified? */
  122.     *s++ = '\0';        /* yes, tie off port */
  123.     port = strtol (s,&s,10);    /* parse port */
  124.     if (s && *s) {
  125.       sprintf (tmp,"Junk after port number: %.80s",s);
  126.       mm_log (tmp,ERROR);
  127.       return NIL;
  128.     }
  129.     sin.sin_port = htons (port);
  130.   }
  131.                 /* copy port number in network format */
  132.   else sin.sin_port = sv ? sv->s_port : htons (port);
  133.  
  134.   /* The domain literal form is used (rather than simply the dotted decimal
  135.      as with other Unix programs) because it has to be a valid "host name"
  136.      in mailsystem terminology. */
  137.                 /* look like domain literal? */
  138.   if (host[0] == '[' && host[(strlen (host))-1] == ']') {
  139.     strcpy (hostname,host+1);    /* yes, copy number part */
  140.     hostname[(strlen (hostname))-1] = '\0';
  141.     if ((sin.sin_addr.s_addr = inet_addr (hostname)) != -1) {
  142.       sin.sin_family = AF_INET;    /* family is always Internet */
  143.       strcpy (hostname,host);    /* hostname is user's argument */
  144.     }
  145.     else {
  146.       sprintf (tmp,"Bad format domain-literal: %.80s",host);
  147.       mm_log (tmp,ERROR);
  148.       return NIL;
  149.     }
  150.   }
  151.   else {            /* lookup host name, note that brain-dead Unix
  152.                    requires lowercase! */
  153.     strcpy (hostname,host);    /* in case host is in write-protected memory */
  154.     i = (int) alarm (0);    /* quell alarms */
  155.     if ((host_name = gethostbyname (lcase (hostname)))) {
  156.       alarm (i);        /* restore alarms */
  157.                 /* copy address type */
  158.       sin.sin_family = host_name->h_addrtype;
  159.                 /* copy host name */
  160.       strcpy (hostname,host_name->h_name);
  161.                 /* copy host addresses */
  162.       memcpy (&sin.sin_addr,host_name->h_addr,host_name->h_length);
  163.     }
  164.     else {
  165.       alarm (i);        /* restore alarms */
  166.       sprintf (tmp,"No such host as %.80s",host);
  167.       mm_log (tmp,ERROR);
  168.       return NIL;
  169.     }
  170.   }
  171.  
  172.                 /* get a TCP stream */
  173.   if ((sock = socket (sin.sin_family,SOCK_STREAM,pt ? pt->p_proto : 0)) < 0) {
  174.     sprintf (tmp,"Unable to create TCP socket: %s",strerror (errno));
  175.     mm_log (tmp,ERROR);
  176.     return NIL;
  177.   }
  178.   flgs = fcntl (sock,F_GETFL,0);/* get current socket flags */
  179.   fcntl (sock,F_SETFL,flgs | FNDELAY);
  180.                 /* open connection */
  181.   while ((i = connect (sock,(struct sockaddr *) &sin,sizeof (sin))) < 0 &&
  182.      errno == EINTR);
  183.   if (i < 0) switch (errno) {    /* failed? */
  184.   case EAGAIN:            /* DG brain damage */
  185.   case EINPROGRESS:
  186.   case EISCONN:
  187.   case EADDRINUSE:
  188.     break;            /* well, not really, it was interrupted */
  189.   default:
  190.     sprintf (tmp,"Can't connect to %.80s,%d: %s",hostname,port,
  191.          strerror (errno));
  192.     mm_log (tmp,ERROR);
  193.     close (sock);        /* flush socket */
  194.     return NIL;
  195.   }
  196.   FD_SET (sock,&fds);        /* block for error or writeable */
  197.   FD_SET (sock,&efds);
  198.   while (((i = select (sock+1,0,&fds,&efds,tmo.tv_sec ? &tmo : 0)) < 0) &&
  199.      (errno == EINTR));
  200.   if (i > 0) {            /* success, make sure really connected */
  201.     fcntl (sock,F_SETFL,flgs);    /* restore blocking status */
  202. #ifndef SOLARISKERNELBUG
  203.                 /* get socket status */
  204.     while ((i = read (sock,tmp,0)) < 0 && errno == EINTR);
  205.     if (!i) i = 1;        /* make success if the read is OK */
  206. #endif
  207.   }
  208.   if (i <= 0) {            /* timeout or error? */
  209.     sprintf (tmp,"Can't connect to %.80s,%d: %s",hostname,port,
  210.          strerror (i ? errno : ETIMEDOUT));
  211.     mm_log (tmp,ERROR);
  212.     close (sock);        /* flush socket */
  213.     return NIL;
  214.   }
  215.  
  216.                 /* create TCP/IP stream */
  217.   stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM));
  218.                 /* copy official host name */
  219.   stream->host = cpystr (hostname);
  220.                 /* get local name */
  221.   gethostname (tmp,MAILTMPLEN-1);
  222.   stream->localhost = cpystr ((host_name = gethostbyname (tmp)) ?
  223.                   host_name->h_name : tmp);
  224.   stream->port = port;        /* port number */
  225.                 /* init sockets */
  226.   stream->tcpsi = stream->tcpso = sock;
  227.   stream->ictr = 0;        /* init input counter */
  228.   return stream;        /* return success */
  229. }
  230.  
  231. /* TCP/IP authenticated open
  232.  * Accepts: host name
  233.  *        service name
  234.  *        returned user name buffer
  235.  * Returns: TCP/IP stream if success else NIL
  236.  */
  237.  
  238. TCPSTREAM *tcp_aopen (host,service,usrbuf)
  239.     char *host;
  240.     char *service;
  241.     char *usrbuf;
  242. {
  243.   TCPSTREAM *stream = NIL;
  244.   struct hostent *host_name;
  245.   char *user = (char *) mail_parameters (NIL,GET_USERNAMEBUF,NIL);
  246.   char hostname[MAILTMPLEN];
  247.   int i;
  248.   int pipei[2],pipeo[2];
  249.   struct timeval tmo;
  250.   fd_set fds,efds;
  251.   if (!(tmo.tv_sec = rshtimeout)) return NIL;
  252.   tmo.tv_usec = 0;
  253.   FD_ZERO (&fds);        /* initialize selection vector */
  254.   FD_ZERO (&efds);        /* handle errors too */
  255.   /* The domain literal form is used (rather than simply the dotted decimal
  256.      as with other Unix programs) because it has to be a valid "host name"
  257.      in mailsystem terminology. */
  258.                 /* look like domain literal? */
  259.   if (host[0] == '[' && host[i = (strlen (host))-1] == ']') {
  260.     strcpy (hostname,host+1);/* yes, copy without brackets */
  261.     hostname[i-1] = '\0';
  262.   }
  263.                 /* note that Unix requires lowercase! */
  264.   else if (host_name = gethostbyname (lcase (strcpy (hostname,host))))
  265.     strcpy (hostname,host_name->h_name);
  266.                 /* make command pipes */
  267.   if (pipe (pipei) < 0) return NIL;
  268.   if (pipe (pipeo) < 0) {
  269.     close (pipei[0]); close (pipei[1]);
  270.     return NIL;
  271.   }
  272.   if ((i = fork ()) < 0) {    /* make inferior process */
  273.     close (pipei[0]); close (pipei[1]);
  274.     close (pipeo[0]); close (pipeo[1]);
  275.     return NIL;
  276.   }
  277.   if (!i) {            /* if child */
  278.     if (!fork ()) {        /* make grandchild so it's inherited by init */
  279.       int maxfd = max (20,max (max(pipei[0],pipei[1]),max(pipeo[0],pipeo[1])));
  280.       dup2 (pipei[1],1);    /* parent's input is my output */
  281.       dup2 (pipei[1],2);    /* parent's input is my error output too */
  282.       dup2 (pipeo[0],0);    /* parent's output is my input */
  283.                 /* close all unnecessary descriptors */
  284.       for (i = 3; i <= maxfd; i++) close (i);
  285.       setpgrp (0,getpid ());    /* be our own process group */
  286.       if (user && *user)    /* now run it */
  287.     execl (RSHPATH,RSH,hostname,"-l",user,"exec",service,0);
  288.       execl (RSHPATH,RSH,hostname,"exec",service,0);
  289.     }
  290.     _exit (1);            /* child is done */
  291.   }
  292.  
  293.   grim_pid_reap (i,NIL);    /* reap child; grandchild now owned by init */
  294.   close (pipei[1]);        /* close child's side of the pipes */
  295.   close (pipeo[0]);
  296.                 /* create TCP/IP stream */
  297.   stream = (TCPSTREAM *) fs_get (sizeof (TCPSTREAM));
  298.                 /* copy official host name */
  299.   stream->host = cpystr (hostname);
  300.                 /* get local name */
  301.   gethostname (hostname,MAILTMPLEN-1);
  302.   stream->localhost = cpystr ((host_name = gethostbyname (hostname)) ?
  303.                   host_name->h_name : hostname);
  304.   stream->tcpsi = pipei[0];    /* init sockets */
  305.   stream->tcpso = pipeo[1];
  306.   stream->ictr = 0;        /* init input counter */
  307.   stream->port = 0xffffffff;    /* no port number */
  308.   FD_SET (stream->tcpsi,&fds);    /* set bit in selection vector */
  309.   FD_SET (stream->tcpsi,&efds);    /* set bit in error selection vector */
  310.   while (((i = select (stream->tcpsi+1,&fds,0,&efds,&tmo)) < 0) &&
  311.      (errno == EINTR));
  312.   if (i <= 0) {            /* timeout or error? */
  313.     mm_log (i ? "error in rsh to IMAP server" : "rsh to IMAP server timed out",
  314.         WARN);
  315.     tcp_close (stream);        /* punt stream */
  316.     stream = NIL;
  317.   }
  318.                 /* return user name */
  319.   strcpy (usrbuf,(user && *user) ? user : myusername ());
  320.   return stream;        /* return success */
  321. }
  322.  
  323. /* TCP/IP receive line
  324.  * Accepts: TCP/IP stream
  325.  * Returns: text line string or NIL if failure
  326.  */
  327.  
  328. char *tcp_getline (stream)
  329.     TCPSTREAM *stream;
  330. {
  331.   int n,m;
  332.   char *st,*ret,*stp;
  333.   char c = '\0';
  334.   char d;
  335.                 /* make sure have data */
  336.   if (!tcp_getdata (stream)) return NIL;
  337.   st = stream->iptr;        /* save start of string */
  338.   n = 0;            /* init string count */
  339.   while (stream->ictr--) {    /* look for end of line */
  340.     d = *stream->iptr++;    /* slurp another character */
  341.     if ((c == '\015') && (d == '\012')) {
  342.       ret = (char *) fs_get (n--);
  343.       memcpy (ret,st,n);    /* copy into a free storage string */
  344.       ret[n] = '\0';        /* tie off string with null */
  345.       return ret;
  346.     }
  347.     n++;            /* count another character searched */
  348.     c = d;            /* remember previous character */
  349.   }
  350.                 /* copy partial string from buffer */
  351.   memcpy ((ret = stp = (char *) fs_get (n)),st,n);
  352.                 /* get more data from the net */
  353.   if (!tcp_getdata (stream)) fs_give ((void **) &ret);
  354.                 /* special case of newline broken by buffer */
  355.   else if ((c == '\015') && (*stream->iptr == '\012')) {
  356.     stream->iptr++;        /* eat the line feed */
  357.     stream->ictr--;
  358.     ret[n - 1] = '\0';        /* tie off string with null */
  359.   }
  360.                 /* else recurse to get remainder */
  361.   else if (st = tcp_getline (stream)) {
  362.     ret = (char *) fs_get (n + 1 + (m = strlen (st)));
  363.     memcpy (ret,stp,n);        /* copy first part */
  364.     memcpy (ret + n,st,m);    /* and second part */
  365.     fs_give ((void **) &stp);    /* flush first part */
  366.     fs_give ((void **) &st);    /* flush second part */
  367.     ret[n + m] = '\0';        /* tie off string with null */
  368.   }
  369.   return ret;
  370. }
  371.  
  372. /* TCP/IP receive buffer
  373.  * Accepts: TCP/IP stream
  374.  *        size in bytes
  375.  *        buffer to read into
  376.  * Returns: T if success, NIL otherwise
  377.  */
  378.  
  379. long tcp_getbuffer (stream,size,buffer)
  380.     TCPSTREAM *stream;
  381.     unsigned long size;
  382.     char *buffer;
  383. {
  384.   unsigned long n;
  385.   char *bufptr = buffer;
  386.   while (size > 0) {        /* until request satisfied */
  387.     if (!tcp_getdata (stream)) return NIL;
  388.     n = min (size,stream->ictr);/* number of bytes to transfer */
  389.                 /* do the copy */
  390.     memcpy (bufptr,stream->iptr,n);
  391.     bufptr += n;        /* update pointer */
  392.     stream->iptr +=n;
  393.     size -= n;            /* update # of bytes to do */
  394.     stream->ictr -=n;
  395.   }
  396.   bufptr[0] = '\0';        /* tie off string */
  397.   return T;
  398. }
  399.  
  400. /* TCP/IP receive data
  401.  * Accepts: TCP/IP stream
  402.  * Returns: T if success, NIL otherwise
  403.  */
  404.  
  405. long tcp_getdata (stream)
  406.     TCPSTREAM *stream;
  407. {
  408.   int i;
  409.   fd_set fds,efds;
  410.   struct timeval tmo;
  411.   time_t t = time (0);
  412.   tmo.tv_sec = tcptimeout_read;
  413.   tmo.tv_usec = 0;
  414.   FD_ZERO (&fds);        /* initialize selection vector */
  415.   FD_ZERO (&efds);        /* handle errors too */
  416.   if (stream->tcpsi < 0) return NIL;
  417.   while (stream->ictr < 1) {    /* if nothing in the buffer */
  418.     FD_SET (stream->tcpsi,&fds);/* set bit in selection vector */
  419.     FD_SET(stream->tcpsi,&efds);/* set bit in error selection vector */
  420.     errno = NIL;        /* block and read */
  421.     while (((i = select (stream->tcpsi+1,&fds,0,&efds,tmo.tv_sec ? &tmo:0))<0)
  422.        && (errno == EINTR));
  423.     if (!i) {            /* timeout? */
  424.       if (tcptimeout && ((*tcptimeout) (time (0) - t))) continue;
  425.       else return tcp_abort (stream);
  426.     }
  427.     if (i < 0) return tcp_abort (stream);
  428.     while (((i = read (stream->tcpsi,stream->ibuf,BUFLEN)) < 0) &&
  429.        (errno == EINTR));
  430.     if (i < 1) return tcp_abort (stream);
  431.     stream->iptr = stream->ibuf;/* point at TCP buffer */
  432.     stream->ictr = i;        /* set new byte count */
  433.   }
  434.   return T;
  435. }
  436.  
  437. /* TCP/IP send string as record
  438.  * Accepts: TCP/IP stream
  439.  *        string pointer
  440.  * Returns: T if success else NIL
  441.  */
  442.  
  443. long tcp_soutr (stream,string)
  444.     TCPSTREAM *stream;
  445.     char *string;
  446. {
  447.   return tcp_sout (stream,string,(unsigned long) strlen (string));
  448. }
  449.  
  450.  
  451. /* TCP/IP send string
  452.  * Accepts: TCP/IP stream
  453.  *        string pointer
  454.  *        byte count
  455.  * Returns: T if success else NIL
  456.  */
  457.  
  458. long tcp_sout (stream,string,size)
  459.     TCPSTREAM *stream;
  460.     char *string;
  461.     unsigned long size;
  462. {
  463.   int i;
  464.   fd_set fds;
  465.   struct timeval tmo;
  466.   time_t t = time (0);
  467.   tmo.tv_sec = tcptimeout_write;
  468.   tmo.tv_usec = 0;
  469.   FD_ZERO (&fds);        /* initialize selection vector */
  470.   if (stream->tcpso < 0) return NIL;
  471.   while (size > 0) {        /* until request satisfied */
  472.     FD_SET (stream->tcpso,&fds);/* set bit in selection vector */
  473.     errno = NIL;        /* block and write */
  474.     while (((i = select (stream->tcpso+1,0,&fds,0,tmo.tv_sec ? &tmo : 0)) < 0)
  475.        && (errno == EINTR));
  476.     if (!i) {            /* timeout? */
  477.       if (tcptimeout && ((*tcptimeout) (time (0) - t))) continue;
  478.       else return tcp_abort (stream);
  479.     }
  480.     if (i < 0) return tcp_abort (stream);
  481.     while (((i = write (stream->tcpso,string,size)) < 0) &&
  482.        (errno == EINTR));
  483.     if (i < 0) return tcp_abort (stream);
  484.     size -= i;            /* how much we sent */
  485.     string += i;
  486.   }
  487.   return T;            /* all done */
  488. }
  489.  
  490. /* TCP/IP close
  491.  * Accepts: TCP/IP stream
  492.  */
  493.  
  494. void tcp_close (stream)
  495.     TCPSTREAM *stream;
  496. {
  497.   tcp_abort (stream);        /* nuke the stream */
  498.                 /* flush host names */
  499.   fs_give ((void **) &stream->host);
  500.   fs_give ((void **) &stream->localhost);
  501.   fs_give ((void **) &stream);    /* flush the stream */
  502. }
  503.  
  504.  
  505. /* TCP/IP abort stream
  506.  * Accepts: TCP/IP stream
  507.  * Returns: NIL always
  508.  */
  509.  
  510. long tcp_abort (stream)
  511.     TCPSTREAM *stream;
  512. {
  513.   int i;
  514.   if (stream->tcpsi >= 0) {    /* no-op if no socket */
  515.     close (stream->tcpsi);    /* nuke the socket */
  516.     if (stream->tcpsi != stream->tcpso) close (stream->tcpso);
  517.     stream->tcpsi = stream->tcpso = -1;
  518.   }
  519.   return NIL;
  520. }
  521.  
  522. /* TCP/IP get host name
  523.  * Accepts: TCP/IP stream
  524.  * Returns: host name for this stream
  525.  */
  526.  
  527. char *tcp_host (stream)
  528.     TCPSTREAM *stream;
  529. {
  530.   return stream->host;        /* return host name */
  531. }
  532.  
  533.  
  534. /* TCP/IP return port for this stream
  535.  * Accepts: TCP/IP stream
  536.  * Returns: port number for this stream
  537.  */
  538.  
  539. long tcp_port (stream)
  540.     TCPSTREAM *stream;
  541. {
  542.   return stream->port;        /* return port number */
  543. }
  544.  
  545.  
  546. /* TCP/IP get local host name
  547.  * Accepts: TCP/IP stream
  548.  * Returns: local host name
  549.  */
  550.  
  551. char *tcp_localhost (stream)
  552.     TCPSTREAM *stream;
  553. {
  554.   return stream->localhost;    /* return local host name */
  555. }
  556.  
  557.  
  558. /* TCP/IP get server host name
  559.  * Accepts: pointer to destination
  560.  * Returns: string pointer if got results, else NIL
  561.  */
  562.  
  563. char *tcp_clienthost (dst)
  564.     char *dst;
  565. {
  566.   struct hostent *hn;
  567.   struct sockaddr_in from;
  568.   int fromlen = sizeof (from);
  569.   if (getpeername (0,(struct sockaddr *) &from,&fromlen)) return "UNKNOWN";
  570.   strncpy (dst,(hn = gethostbyaddr ((char *) &from.sin_addr,
  571.                    sizeof (struct in_addr),from.sin_family)) ?
  572.        hn->h_name : inet_ntoa (from.sin_addr),80);
  573.   return dst;
  574. }
  575.